package com.skynet.supervision.aop;

import cn.hutool.core.util.ReUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.skynet.supervision.common.ErrCode;
import com.skynet.supervision.common.Result;
import com.skynet.supervision.exceptions.CheckedException;
import com.skynet.supervision.utils.Errors;
import com.skynet.supervision.utils.Strings;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolationException;
import java.time.format.DateTimeParseException;
import java.util.Objects;
import java.util.stream.Collectors;

//全局异常处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(CheckedException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    Result exceptionHandler(CheckedException e) {
        log.error("请求接口异常(CheckedException)", e);
        return e.unwrap();
    }

    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(MethodArgumentNotValidException e) {
        log.error("请求参数异常(MethodArgumentNotValidException)", e);
        return
                Objects.requireNonNull(
                                Errors.wrapValidateException(e.getBindingResult()))
                        .unwrap()
                        .withCode(ErrCode.BAD_PARAM.getCode());
    }

    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(ConstraintViolationException e) {
        log.error("请求异常(ConstraintViolationException)", e);
        @SuppressWarnings("unchecked")
        val errors = e.getConstraintViolations().stream()
                .map(it -> (javax.validation.ConstraintViolation<Object>) it)
                .collect(Collectors.toSet());
        return Objects.requireNonNull(Errors.wrapValidateException(errors))
                .unwrap()
                .withCode(ErrCode.BAD_PARAM.getCode());
    }

    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(IllegalArgumentException e) {
        log.error("请求参数异常(IllegalArgumentException)", e);
        String message = e.getMessage();
        String s = ".*";
        return ReUtil.isMatch(s + "[一-\u9fff]" + s, message) ? Result.failure(ErrCode.BAD_PARAM.getCode(), message) : Result.failure(ErrCode.BAD_PARAM.getCode(), "在效验参数的时候进行断言验证是提示了一个错误,请联系开发人员添加提示:" + message);
    }

    @ExceptionHandler({BindException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(BindException e) {
        log.error("请求参数异常(BindException)", e);
        return
                Objects.requireNonNull(
                                Errors.wrapValidateException(e.getBindingResult()))
                        .unwrap()
                        .withCode(Errors.EBAD_PARAMS);
    }

    @ExceptionHandler({JsonProcessingException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(JsonProcessingException e) {
        log.error("请求参数异常(JsonProcessingException)", e);
        return Result.failure(ErrCode.BAD_PARAM.getCode(), Strings.sprintf("请求参数错误，{}", e.getCause().getMessage()));
    }

    @ExceptionHandler({HttpMessageNotReadableException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(HttpMessageNotReadableException e) {
        log.error("请求参数异常(HttpMessageNotReadableException)", e);
        return Result.failure(ErrCode.BAD_PARAM.getCode(), Strings.sprintf("请求参数错误，{}", e.getCause().getMessage()));
    }

    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(MethodArgumentTypeMismatchException e) {
        log.error("请求参数异常(MethodArgumentTypeMismatchException)", e);
        return Result.failure(ErrCode.BAD_PARAM.getCode(), Strings.sprintf("请求参数错误，{}", e.getCause().getMessage()));
    }

    @ExceptionHandler({MissingServletRequestParameterException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(MissingServletRequestParameterException e) {
        log.error("请求参数异常(MissingServletRequestParameterException)", e);
        return Result.failure(ErrCode.BAD_PARAM.getCode(), "缺少必填参数:" + e.getParameterName());
    }

    @ExceptionHandler({DataAccessException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(DataAccessException e) {
        log.error("数据库异常(DataAccessException)", e);
        return Result.failure(ErrCode.DB_ERROR.getCode(), "数据库异常," + e.getMessage());
    }

    @ExceptionHandler({DateTimeParseException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(DateTimeParseException e) {
        log.error("请求参数异常(DateTimeParseException)", e);
        return Result.failure(ErrCode.BAD_PARAM.getCode(), "时间格式有误");
    }

    @ExceptionHandler({Exception.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result exceptionHandler(Exception e) {
        log.error("请求接口异常(Exception)", e);
        val rootCause = Errors.getRootCause(e);
        if (rootCause instanceof CheckedException) {
            return ((CheckedException) rootCause).unwrap();
        }
        return Result.failure(ErrCode.INTERNAL_ERROR.getCode(), "请求异常" + e.getMessage());
    }
}
